#include "lom-binary-in-stream.hh"
#include "binary-in-stream.hh"
#include "lom-value-factory.hh"
#include "lom-class-manager.hh"
#include "lom-instance.hh"
#include "lom-property.hh"
#include "lom-class.hh"
#include "lom-stream-handle-map.hh"

using namespace licada;
using namespace lom;
using namespace boost;

namespace lom
{
    template< typename TDataType > 
    inline void read( IBinaryInStream& proxy, TDataType& val )
    {
	uint8* data = (uint8*)&val;
	for ( unsigned index = 0; index < sizeof( val ); ++index )
	    proxy >> data[index];
    }
    template< typename TDataType >
    inline void readVec( IBinaryInStream& proxy, std::vector<TDataType>& val )
    {
	uint32 len;
	read( proxy, len );
	val.resize( len );
	do_all( val, bind( read<TDataType>, ref(proxy), _1 ) );
    }

    void LomBinaryInStream::operator>>( bool& val ) {
	uint8 temp;
	(*inStream) >> temp;
	val = (bool) temp;
    }
    void LomBinaryInStream::operator>>( double& val ) {
	read( *inStream, val );
    }
    void LomBinaryInStream::operator>>( uint8& val ) {
	(*inStream) >> val;
    }
    void LomBinaryInStream::operator>>( uint16& val ) {
	read( *inStream, val );
    }
    void LomBinaryInStream::operator>>( uint32& val ) {
	read(*inStream, val );
    }
    void LomBinaryInStream::operator>>( int64& val ) {
	read(*inStream, val );
    }
    void LomBinaryInStream::operator>>( TDoubleVector& val ) {
	readVec( *inStream, val );
    }
    void LomBinaryInStream::operator>>( TInt64Vector& val ) {
	readVec( *inStream, val );
    }
    void LomBinaryInStream::operator>>( TString& val ) {
	readVec( *inStream, val );
    }
    void LomBinaryInStream::operator>>( TStringVector& val ) {
	uint32 len;
	read( *inStream, len );
	val.resize( len );
	for ( unsigned index = 0; index < len; ++index )
	    readVec( *inStream, val[index] );
    }
    void LomBinaryInStream::operator>>( TUInt32Vector& val ) {
	readVec( *inStream, val );
    }
    void LomBinaryInStream::operator>>( TDataVector& val ) {
	readVec( *inStream, val );
    }
    void LomBinaryInStream::operator>>( TNameStr& val ) {
	uint32 len;
	read( *inStream, len );
	val.resize( len );
	for ( unsigned index = 0; index < len; ++index )
	    read( *inStream, val[index] );
    }

    void LomBinaryInStream::operator>>( EDataType& dtype ) {
	uint8 val;
	*this >> val;
	dtype = (EDataType)val;
    }
    
    void LomBinaryInStream::operator>>( LomValue*& val ) {
	EDataType type;
	*this >> type;
	
	if ( type == EDataTypeNone ) {
	    val = NULL;
	    return; 
	}
	val = factory->create( type );
	switch( type )
	{
	case EDataTypeBool:
	    *this >> val->getBool();
	    break;
	case EDataTypeString:
	    *this >> val->getString();
	    break;
	case EDataTypeStringV:
	    *this >> val->getStringV();
	    break;
	case EDataTypeHandle:
	    *this >> tempHandle;
	    tempHandle = incomingMap->streamToWorld( tempHandle );
	    val->set( tempHandle );
	    break;
	case EDataTypeHandleV:
	    *this >> tempHandleV;
	    std::transform( tempHandleV.begin(), tempHandleV.end()
			    , tempHandleV.begin()
			    , bind(&ILomIncomingHandleMap::streamToWorld
				   , incomingMap, _1 ));
	    val->set( tempHandleV );
	    break;
	case EDataTypeDouble:
	    *this >> val->getDouble();
	    break;
	case EDataTypeDoubleV:
	    *this >> val->getDoubleV();
	    break;
	case EDataTypeInt64:
	    *this >> val->getInt64();
	    break;
	case EDataTypeInt64V:
	    *this >> val->getInt64V();
	    break;
	case EDataTypeDataV:
	    *this >> val->getDataV();
	    break;
	default:
	    throw UnrecognizedDatatype( type );
	}
    }
    static inline void readVal(LomBinaryInStream* str, LomValue*& val ) {
	*str >> val;
    }
    void LomBinaryInStream::operator>>( TValueVector& val) {
	uint32 len;
	*this >> len;
	val.resize( len );
	do_all( val, bind( readVal, this, _1 ) );
    }
    void LomBinaryInStream::operator>>( lom::LomInstance& val ) {
	*this >> tempStr;
	val.setClass( clsManager->getClass( tempStr ) );
	*this >> val.getValues();
    }
    void LomBinaryInStream::operator>>( lom::LomClass& val )
    {
	*this >> tempStr;
	val.setName( tempStr );
	*this >> val.getProperties();
	*this >> val.getDefaults();
    }
    static inline void readProp( LomBinaryInStream* str, LomProperty& val )
    {
	*str >> val.name;
	*str >> val.type;
    }
    void LomBinaryInStream::operator>>( TPropertyVector& val )
    {
	uint32 len;
	*this >> len;
	val.resize( len );
	do_all( val, bind( readProp, this, _1 ) );
    }
}
